Skip to content

Conversation

@AIC-BV
Copy link
Contributor

@AIC-BV AIC-BV commented Jan 22, 2024

Add the possibility to define the popup size and its custom CSS classes.
You can now define these properties at the manage part of the config_relation.yaml:

myRelation:
    manage:
        size: giant
        cssClass: test

See all popup sizes at https://wintercms.com/docs/v1.2/ui/controls/popup#data-attributes

size already existed, but was hardcoded and not dynamically filled in.
cssClass is added.

Summary by CodeRabbit

Release Notes

  • New Features
    • Popups now support custom CSS class application for flexible styling customization
    • Added option to dismiss popups by clicking the backdrop area
    • Relation record viewing now supports configurable popup size, CSS styling, and backdrop dismissal options

✏️ Tip: You can customize this high-level summary in your review settings.

@AIC-BV AIC-BV marked this pull request as draft January 22, 2024 15:31
@mjauvin mjauvin added enhancement PRs that implement a new feature or substantial change needs review Issues/PRs that require a review from a maintainer labels Jan 22, 2024
@mjauvin mjauvin self-assigned this Jan 22, 2024
@LukeTowers
Copy link
Member

@AIC-BV @mjauvin what's left on this?

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Feb 21, 2024

@LukeTowers Should be finished

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Feb 22, 2024

Could add allowDismiss (see comments)
#1053

@AIC-BV AIC-BV changed the title Relation controller popup size Relation controller popup: fix size dismiss Feb 23, 2024
@AIC-BV AIC-BV changed the title Relation controller popup: fix size dismiss Relation controller popup: fix size and cssClass, add allowDismiss Feb 23, 2024
@AIC-BV AIC-BV marked this pull request as ready for review February 23, 2024 09:37
@AIC-BV AIC-BV changed the title Relation controller popup: fix size and cssClass, add allowDismiss Relation controller popup: fix size, add cssClass and allowDismiss Feb 23, 2024
@LukeTowers
Copy link
Member

@AIC-BV just a thought, is there a way we could integrate with the change monitor used in the cms section for the tabs to detect when a change has happened inside of a modal and warn / prevent the dismiss behaviour from being triggered in those cases?

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Feb 26, 2024

@AIC-BV just a thought, is there a way we could integrate with the change monitor used in the cms section for the tabs to detect when a change has happened inside of a modal and warn / prevent the dismiss behaviour from being triggered in those cases?

I assume something like this could be a basic for that kind of behaviour

if (this.options.allowDismiss) {
    modal.on('click', function(e) {
        const target = e.target;
        if (target.classList.contains('control-popup')) {

            if (!modal.dataset.hasChanges) {
                modal.hide()
                $('.popup-backdrop').remove()
                $(document.body).removeClass('modal-open')
            } else {
                // popup warning them about unsaved changes
            }

        }
    });

    const inputs = modal.querySelectorAll('input')
    for (const input of inputs) {
        input.addEventListener('change', function() {
            modal.dataset.hasChanges = true
        })
    }
}

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Mar 22, 2024

Thoughts @LukeTowers @mjauvin ?

@LukeTowers
Copy link
Member

@AIC-BV seems fine, have you tested it out?

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Apr 7, 2024

I'm using everything except the latest comment: #1044 (comment)

@LukeTowers
Copy link
Member

@AIC-BV can we remove allowDismiss from this PR for now then? I don't want to support it unless it can be a bit safer first.

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Apr 16, 2024

If its ok for you I will add the popup shortly instead

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Apr 30, 2024

@AIC-BV can we remove allowDismiss from this PR for now then? I don't want to support it unless it can be a bit safer first.

Can you help me getting started?
Don't know how to do this using Winter UI.

Also, will this be enough?
For example: the code below will notice 'changes' when input has been changed, but if it is changed back to its original value, it will still be marked as changed etc. There are some loopholes to this simple logic.

PS:
The PR as is, works perfectly fine. Have been using it a lot, by many people, in production for a month now, but it shows no popup if they have unchanged changes (but it doesn't occur here)

if (this.options.allowDismiss) {
    modal.on('click', function(e) {
        const target = e.target;
        if (target.classList.contains('control-popup')) {

            if (!modal.dataset.hasChanges) {
                modal.hide()
                $('.popup-backdrop').remove()
                $(document.body).removeClass('modal-open')
            } else {
            
                // HOW CAN I DO THIS USING WINTER UI?
                // popup warning them about unsaved changes
                // where they can press 'Cancel' or 'Continue anyways'
            }

        }
    });

    const inputs = modal.querySelectorAll('input')
    for (const input of inputs) {
        input.addEventListener('change', function() {
            modal.dataset.hasChanges = 'true'
        })
    }
}

https://wintercms.com/docs/v1.2/ui/controls/popup#examples

@LukeTowers
Copy link
Member

There's existing code for tracking changes here: https://wintercms.com/docs/v1.2/ui/script/input-monitor

@AIC-BV
Copy link
Contributor Author

AIC-BV commented May 15, 2024

Was making another relation popup and added these properties in the relation_config:
Can't stress enough how good it works and how nice it is.

allowDismiss: true
cssClass: huge-popup

I tried the change monitor and it doesn't show the warning when closing the popup.
The change monitor has this 'issue' as well

For example: the code below will notice 'changes' when input has been changed, but if it is changed back to its original value, it will still be marked as changed

What is left for finishing this PR is a way to show a popup warning the user about unsaved changes using either Input Monitor or custom written JS.

if (this.options.allowDismiss) {
    modal.on('click', function(e) {
        const target = e.target;
        if (target.classList.contains('control-popup')) {

            if (!modal.dataset.hasChanges) {
                modal.hide()
                $('.popup-backdrop').remove()
                $(document.body).removeClass('modal-open')
            } else {
            
                // HOW CAN I DO THIS USING WINTER UI?
                // popup warning them about unsaved changes
                // where they can press 'Cancel' or 'Continue anyways'
                alert('UNSAVED CHANGES!')
               
            }

        }
    });

    const inputs = modal.querySelectorAll('input')
    for (const input of inputs) {
        input.addEventListener('change', function() {
            modal.dataset.hasChanges = 'true'
        })
    }
}

@LukeTowers
Copy link
Member

@jaxwilko any input?

@jaxwilko
Copy link
Member

@jaxwilko any input?

You can use Snowboard.flash() to trigger the flash messaging, I believe it's available in the backend.

Apart from that looks cool, I'll check it out and have a play when I have time :)

AIC-BV added 2 commits August 28, 2024 11:02
Only trigger on mousedown, because when selecting text to copy you sometimes leave the modal and that would close it.
@AIC-BV
Copy link
Contributor Author

AIC-BV commented Aug 28, 2024

Strongly suggest to try it out :)
Works great and is being used here 8 hours/day by 3-4 collegues

@LukeTowers
Copy link
Member

@AIC-BV are you able to record a loom or something showing the new functionality and make a PR to the docs? If you can get those done then I should be able to quickly review and merge this before I push out 1.2.7.

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Aug 30, 2024

  1. This PR fixes the 'size' attribute, which was hardcoded before
  2. Adds the 'cssClass' attribute: allowing you to customise the popup (I make it much bigger because it has important data for production)
  3. And adds support for 'allowDismiss': https://www.loom.com/share/3c10c530854f4e0597f4dd00c632a5cc?sid=08f8142e-55b0-42fc-aae6-fab1b5820c77

allowDismiss = Clicking next to the modal closes it (on mousedown because if you drag to select something and release the mouse button outside the modal, it would close the modal too)
Great for reading the data in the record and not having to scroll down to close it

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Aug 30, 2024

wintercms/docs#207

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Jul 4, 2025

Ran composer update and this got overwritten ofcourse.
We still use this every single day

@LukeTowers
Copy link
Member

LukeTowers commented Jul 16, 2025

@AIC-BV waiting on the docs PR to get updated / my comment to be responded to: https://github.com/wintercms/docs/pull/207/files#r1739737060. Will also need to update from develop and recompile the JS.

@AIC-BV
Copy link
Contributor Author

AIC-BV commented Jul 18, 2025

@AIC-BV waiting on the docs PR to get updated / my comment to be responded to: https://github.com/wintercms/docs/pull/207/files#r1739737060. Will also need to update from develop and recompile the JS.

Oops 👀

@mjauvin
Copy link
Member

mjauvin commented Dec 26, 2025

@AIC-BV are we good with all the requested changes? I'd like this to get merged.

@mjauvin mjauvin added this to the 1.2.10 milestone Dec 26, 2025
@AIC-BV
Copy link
Contributor Author

AIC-BV commented Jan 5, 2026

@AIC-BV are we good with all the requested changes? I'd like this to get merged.

I think there is this pending feedback but I don't know what to do with it wintercms/docs#207

@mjauvin
Copy link
Member

mjauvin commented Jan 5, 2026

@AIC-BV are we good with all the requested changes? I'd like this to get merged.

I think there is this pending feedback but I don't know what to do with it wintercms/docs#207

@LukeTowers could you clarify so that can be completed?

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Walkthrough

The relation controller system is extended to support customizable popup modal styling and dismiss behavior. New parameters (size, cssClass, allowDismiss) are propagated from the PHP backend through the JavaScript relation handler to the Popup component, enabling configuration of modal appearance and backdrop-click dismissal directly from the RelationController.

Changes

Cohort / File(s) Summary
Backend Parameter Generation
modules/backend/behaviors/RelationController.php
Extended default click handler to pass three new UI configuration parameters (size, cssClass, allowDismiss) to the JavaScript clickViewListRecord function.
JavaScript Relation Handler
modules/backend/behaviors/relationcontroller/assets/js/winter.relation.js
Updated clickViewListRecord method signature to accept size, cssClass, and allowDismiss parameters; defaults size to 'huge' when not provided; forwards these values to popup configuration.
Popup Component Implementation
modules/system/assets/ui/js/popup.js, modules/system/assets/ui/storm-min.js
Added cssClass and allowDismiss to Popup.DEFAULTS; applies cssClass to modal-dialog element when provided; attaches mousedown listener to backdrop for dismissal when allowDismiss is true.

Sequence Diagram

sequenceDiagram
    participant User
    participant RelationController as RelationController.php
    participant ClickHandler as clickViewListRecord()
    participant Popup as Popup Component
    participant Modal as Modal DOM

    User->>RelationController: Click list record
    RelationController->>ClickHandler: Generate JS call with<br/>size, cssClass, allowDismiss
    ClickHandler->>Popup: Create popup with new<br/>config parameters
    Popup->>Modal: Apply cssClass to<br/>modal-dialog element
    alt allowDismiss enabled
        Popup->>Modal: Attach mousedown listener<br/>to backdrop
        User->>Modal: Click backdrop area
        Modal->>Popup: Trigger dismiss
        Popup->>Modal: Remove modal & cleanup
    end
    Popup-->>User: Show styled modal
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: making the relation controller popup configurable with dynamic size, adding cssClass support, and introducing allowDismiss functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @modules/system/assets/ui/js/popup.js:
- Around line 218-227: The allowDismiss mousedown handler bypasses the Popup
lifecycle by calling jQuery's modal.hide() and manually removing backdrop/body
classes; replace that handler so it calls the Popup instance's hide() method
(use this.hide() bound to the popup context or call popup.hide()), remove manual
$('.popup-backdrop').remove() and $(document.body).removeClass('modal-open')
calls so Bootstrap/Popup cleanup and hide events run, and ensure the hide flow
honors the allowHide/lock checks (use this.lock(true) or check current lock
state before dismissing). If you prefer the safer option requested in PR, remove
or disable the allowDismiss feature entirely in this patch instead of
re-enabling it until InputMonitor (or equivalent unsaved-changes detection) is
integrated.

In @modules/system/assets/ui/storm-min.js:
- Around line 2102-2104: Remove the temporary allowDismiss implementation from
this PR by deleting the conditional block that attaches the mousedown handler
(the code using this.options.allowDismiss and modal.on('mousedown', ...)), and
remove any direct DOM manipulations inside it (the $('.popup-backdrop').remove()
and $(document.body).removeClass('modal-open') calls); leave Popup's existing
hide/dispose behavior untouched (do not add new event listeners or direct
backdrop/body manipulation here). If any configuration/defaults or docs were
added for allowDismiss, remove those as well. If reintroducing the feature
later, attach the handler to an instance property, check popup lock/state and
unsaved-change detection before calling modal.hide(), and remove the handler in
Popup.prototype.dispose rather than manipulating backdrop/body directly.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ecb820 and 929c822.

📒 Files selected for processing (4)
  • modules/backend/behaviors/RelationController.php
  • modules/backend/behaviors/relationcontroller/assets/js/winter.relation.js
  • modules/system/assets/ui/js/popup.js
  • modules/system/assets/ui/storm-min.js
🧰 Additional context used
🧬 Code graph analysis (3)
modules/backend/behaviors/relationcontroller/assets/js/winter.relation.js (1)
modules/backend/classes/FormField.php (1)
  • size (217-221)
modules/system/assets/ui/js/popup.js (1)
modules/system/assets/ui/storm-min.js (1)
  • target (2102-2102)
modules/backend/behaviors/RelationController.php (1)
modules/backend/classes/FormField.php (1)
  • size (217-221)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: windows-latest / PHP 8.4
  • GitHub Check: windows-latest / PHP 8.3
  • GitHub Check: windows-latest / PHP 8.1
  • GitHub Check: ubuntu-latest / PHP 8.4
  • GitHub Check: windows-latest / PHP 8.2
  • GitHub Check: ubuntu-latest / PHP 8.2
  • GitHub Check: ubuntu-latest / PHP 8.3
  • GitHub Check: ubuntu-latest / PHP 8.1
  • GitHub Check: windows-latest / JavaScript
🔇 Additional comments (9)
modules/backend/behaviors/RelationController.php (2)

688-690: LGTM: Config properties correctly retrieved.

The three new configuration properties (size, cssClass, allowDismiss) are properly retrieved from the manage section of the relation config. These values default to null if not specified, which is handled appropriately by the downstream JavaScript code.


692-700: LGTM: Parameter passing is consistent.

The sprintf format string and arguments are correctly aligned. The six parameters match the updated JavaScript function signature in winter.relation.js, enabling the popup configuration to flow from the backend to the frontend.

modules/system/assets/ui/js/popup.js (2)

52-54: LGTM: Safe default values.

The new default values are appropriately conservative. Setting allowDismiss to false by default ensures existing popups maintain their current behavior and only opt-in to the new dismiss functionality.


212-213: LGTM: cssClass implementation is safe.

The cssClass is correctly applied to the modal-dialog element. jQuery's addClass method safely handles the input without XSS risk.

modules/backend/behaviors/relationcontroller/assets/js/winter.relation.js (2)

12-12: LGTM: Function signature extended correctly.

The updated signature accepts the three new parameters (size, cssClass, allowDismiss) that are now passed from the PHP backend. This maintains consistency across the backend-to-frontend data flow.


17-25: The code correctly uses 'huge' as a valid popup size with appropriate CSS styling.

The default size of 'huge' is valid and well-established in the codebase, with CSS class definitions present in multiple stylesheet files. The popup configuration correctly passes all three parameters (size, cssClass, allowDismiss) to the popup plugin.

modules/system/assets/ui/storm-min.js (3)

2100-2100: LGTM: cssClass implementation looks correct.

The cssClass option is properly applied to the modal dialog element, allowing custom styling as documented in the PR objectives.


4683-4683: Clarify unrelated change to listSortable.

This change to the listSortable component appears unrelated to the PR's objectives (relation controller popup configuration). Is this an unintentional side effect from recompiling the minified file, or does it serve a purpose for this PR?

If unintentional, consider reverting this change to keep the PR focused on relation popup enhancements.

Could you confirm whether this change was intentional and how it relates to the popup size/cssClass/allowDismiss features?


2056-2056: Consider disabling or enhancing allowDismiss with change detection.

The allowDismiss implementation allows users to dismiss popups by clicking the backdrop without validating for unsaved changes. While the feature defaults to false (opt-in), if enabled it should integrate with the Input Monitor system to prevent accidental data loss.

The cssClass addition is good and can remain.

Suggested improvements if allowDismiss is kept:

  • Integrate with the Input Monitor (input.monitor.js) to detect unsaved changes before allowing dismissal
  • Show a confirmation dialog if changes are detected
  • Clean up the mousedown event listener on popup disposal to prevent memory leaks

Comment on lines +218 to +227
if (this.options.allowDismiss) {
modal.on('mousedown', function(e) {
const target = e.target;
if (target.classList.contains('control-popup')) {
modal.hide()
$('.popup-backdrop').remove()
$(document.body).removeClass('modal-open')
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: allowDismiss implementation bypasses popup lifecycle and lock mechanism.

The current implementation has several critical flaws:

  1. Line 222 uses modal.hide() (jQuery's hide method) instead of calling the Popup instance's hide() method (line 334). This bypasses:

    • The allowHide lock check (line 340) which prevents closing locked popups
    • The hide event triggers (lines 337-338)
    • The proper cleanup flow managed by Bootstrap modal events
  2. Lines 223-224 manually remove backdrop and body class instead of letting the Bootstrap modal hide events handle cleanup (lines 148-154). This can leave the popup in an inconsistent state.

  3. Missing unsaved changes protection: Per PR discussion, there is no change monitoring to warn users about unsaved data before dismissing. This creates a risk of accidental data loss.

Recommendation: Based on the PR discussion where LukeTowers requested removing allowDismiss until safer handling is implemented, consider either:

  • Remove the allowDismiss feature from this PR
  • Integrate Winter UI Input Monitor to detect unsaved changes before allowing dismiss
  • Use the popup's lock mechanism (this.lock(true)) when changes are detected
🔧 Proposed fix to use proper popup hide method
         if (this.options.allowDismiss) {
+            var self = this
             modal.on('mousedown', function(e) {
                 const target = e.target;
                 if (target.classList.contains('control-popup')) {
-                    modal.hide()
-                    $('.popup-backdrop').remove()
-                    $(document.body).removeClass('modal-open')
+                    // Use the popup instance's hide method to ensure proper cleanup
+                    self.hide()
                 }
             });
         }

Note: This fix addresses the lifecycle bypass but does not solve the missing change monitoring concern.

🤖 Prompt for AI Agents
In @modules/system/assets/ui/js/popup.js around lines 218 - 227, The
allowDismiss mousedown handler bypasses the Popup lifecycle by calling jQuery's
modal.hide() and manually removing backdrop/body classes; replace that handler
so it calls the Popup instance's hide() method (use this.hide() bound to the
popup context or call popup.hide()), remove manual $('.popup-backdrop').remove()
and $(document.body).removeClass('modal-open') calls so Bootstrap/Popup cleanup
and hide events run, and ensure the hide flow honors the allowHide/lock checks
(use this.lock(true) or check current lock state before dismissing). If you
prefer the safer option requested in PR, remove or disable the allowDismiss
feature entirely in this patch instead of re-enabling it until InputMonitor (or
equivalent unsaved-changes detection) is integrated.

Comment on lines +2102 to +2104
if(this.options.allowDismiss){modal.on('mousedown',function(e){const target=e.target;if(target.classList.contains('control-popup')){modal.hide()
$('.popup-backdrop').remove()
$(document.body).removeClass('modal-open')}});}return modal.append(modalDialog.append(modalContent))}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: allowDismiss implementation lacks change detection and proper cleanup.

Multiple concerns with the current allowDismiss implementation:

  1. Data loss risk: No validation for unsaved changes before closing (the primary concern raised in PR discussion)
  2. Memory leak: The mousedown event listener is never removed when the popup is disposed
  3. Direct DOM manipulation: Directly manipulating backdrop and body classes may conflict with the Popup's internal state management

The event listener should be stored and removed in the dispose method. However, per maintainer feedback, the entire allowDismiss feature should be removed from this PR until proper change-monitoring integration is complete.

🐛 Issues to address if keeping this feature

If allowDismiss is kept (against maintainer's request), at minimum:

  1. Store the event handler reference and remove it in Popup.prototype.dispose
  2. Check if the popup is locked before allowing dismissal
  3. Integrate change detection to prevent data loss
  4. Use the Popup's own hide method logic rather than direct DOM manipulation

However, the recommended action is to remove this feature entirely until proper change-monitoring is implemented.

Based on PR discussion where LukeTowers requested removal of allowDismiss until safer implementation.

🤖 Prompt for AI Agents
In @modules/system/assets/ui/storm-min.js around lines 2102 - 2104, Remove the
temporary allowDismiss implementation from this PR by deleting the conditional
block that attaches the mousedown handler (the code using
this.options.allowDismiss and modal.on('mousedown', ...)), and remove any direct
DOM manipulations inside it (the $('.popup-backdrop').remove() and
$(document.body).removeClass('modal-open') calls); leave Popup's existing
hide/dispose behavior untouched (do not add new event listeners or direct
backdrop/body manipulation here). If any configuration/defaults or docs were
added for allowDismiss, remove those as well. If reintroducing the feature
later, attach the handler to an instance property, check popup lock/state and
unsaved-change detection before calling modal.hide(), and remove the handler in
Popup.prototype.dispose rather than manipulating backdrop/body directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement PRs that implement a new feature or substantial change needs review Issues/PRs that require a review from a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants